<template>
  <div v-loading="loading">
    <!-- 左侧树图 -->
    <header class="tree-header">
      <el-input v-model="filterText" clearable placeholder="请输入关键字" class="mr-10px mb-10px" />
      <!-- <el-tooltip class="box-item" effect="dark" content="导入" placement="top" v-if="!simple">
        <el-button :icon="Download" circle @click="imported" />
      </el-tooltip> -->
      <el-popover placement="bottom" v-if="!simple">
        <template #reference>
          <el-button type="primary" :icon="Plus" circle />
        </template>
        <div class="operation-list">
          <p @click="imported"><el-icon>
              <Download />
            </el-icon>导入数据</p>
          <p @click="getTreeData">
            <el-icon>
              <Refresh />
            </el-icon>刷新
          </p>
           <p @click="createMarkdown"><el-icon>
              <Tickets />
            </el-icon>新建Markdown</p>
        </div>
      </el-popover>
    </header>
    <el-tree :data="treeData" :allow-drop="allowDrop" :allow-drag="allowDrag" draggable default-expand-all node-key="id"
      @node-drop="handleDrop" :expand-on-click-node="false" :filter-node-method="filterNode" :props="defaultProps"
      :indent="22" @node-click="handleNodeClick" :show-checkbox="isShowCheckBox" class="tree-com" ref="treeRef"
      @check-change="changeCheckTreeNodes" :check-on-click-node="true">
      <template #default="{ node, data }">
        <span class="custom-tree-node">
          <!-- 项目、模块目录 -->
          <span v-if="['目录'].includes(data.tag)" style="display: flex; align-items: center">
            <span @click="node.expanded = !node.expanded">
              <el-icon v-if="node.expanded" class="case-icon" size="16">
                <FolderOpened />
              </el-icon>
              <el-icon v-else class="case-icon" size="16">
                <Folder />
              </el-icon>
            </span>
            <small>{{ node.label }}</small>
          </span>
          <!-- 接口 -->
          <span v-if="['接口'].includes(data.tag)" style="display: flex; align-items: center"
            :class="{ current: defaultId == data.id }">
            <span class="method" :style="{ color: methodStyle[data.requestType] }">{{ data.requestType }}</span>
            <small>{{ node.label }}<span v-if="data.caseNum" class="ml-5px">({{ data.caseNum }})</span></small>
          </span>
          <span v-if="['用例'].includes(data.tag)" style="display: flex; align-items: center"
            :class="{ current: defaultId == data.id }">
            <img src="@/assets/svgs/case.svg" class="case-icon" />
            <small>{{ node.label }}</small>
          </span>
          <span class="operation" v-if="!simple">
            <el-popover class="operation-popover" v-if="popoverVisible" @before-leave="hidePopver">
              <template #reference>
                <el-icon>
                  <MoreFilled />
                </el-icon>
              </template>
              <div class="operation-list">
                <p @click.stop="append(data)" v-if="data.tag == '目录'"><el-icon>
                    <FolderAdd />
                  </el-icon> 新建子目录 </p>
                <p @click.stop="edit(data)" v-if="['目录', '用例', '接口'].includes(data.tag)"> <el-icon>
                    <Edit />
                  </el-icon>重命名 </p>
                <p @click.stop="createInterface(data)" v-if="data.tag == '目录'"><el-icon>
                    <Cpu />
                  </el-icon> 新建接口 </p>
                <p @click.stop="createCase(data)" v-if="data.tag == '接口'"><el-icon>
                    <Cpu />
                  </el-icon> 新建用例
                </p>
                <p @click.stop="remove(data)" v-if="!data.isRoot"><el-icon>
                    <Remove />
                  </el-icon> 删除 </p>
              </div>
            </el-popover>
          </span>
        </span>
      </template>
    </el-tree>
  </div>

</template>

<script setup lang="ts">
import * as DebugApi from '@/api/debug'
import { MoreFilled, Plus, Download, FolderOpened, Folder, Clock, Edit, Remove, Cpu, FolderAdd, Connection, Link, House, Tickets, Refresh } from '@element-plus/icons-vue'
import type Node from 'element-plus/es/components/tree/src/model/node'
import type { AllowDropType } from 'element-plus/es/components/tree/src/tree.type'
import { useAppStore } from '@/store/modules/app'
import { propTypes } from '@/utils/propTypes'
import { findNodeByParams, findAndModifyNode, filterTreeByTags } from '@/utils/tree'
import { useDebugStore } from '@/store/modules/debug'
const appStore = useAppStore()
const debugStore = useDebugStore()
const projectId = computed(() => appStore.getSelectProject)
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const treeRef = ref()
const filterText = ref('')
const emit = defineEmits(['add-directory', 'edit-directory', 'create-interface', 'create-case', 'node-click', 'delete-node', 'selectNodes', 'import-interface', 'markdown', 'importSetTime'])
const popoverVisible = ref(true)
const defaultId = ref()
const defaultProps = {
  children: 'children',
  label: 'name'
}
const treeData = ref([] as any)
const loading = ref(false)
const props = defineProps({
  simple: propTypes.bool.def(false), // 是否启动简单模式（隐藏操作）
  isShowCheckBox: propTypes.bool.def(false), // 是否展示复选框
  currentTabId: propTypes.number.def(), // 默认选中id
  filterList: propTypes.array.def(['目录', '接口', '用例']) // 显示的层级
})
/** 请求类型颜色定义 */
const methodStyle = ref({
  'GET': '#17b26a',
  'POST': '#ef6820',
  'DELETE': '#f04438',
  'PUT': '#2e90fa',
})
/** 当前页面变化，树节点选中更新 */
watch(() => props.currentTabId, () => {
  defaultId.value = props.currentTabId
})
/**
 * 过滤
 * */
const filterNode = (value, data) => {
  if (!value) return true
  return data.name.includes(value)
}
/** 监听过滤字段变化 */
watch(filterText, (val) => {
  treeRef.value!.filter(val)
})
/** 监听项目id变化 */
watch(() => projectId.value, () => {
  treeData.value = []
  getTreeData()
})

onMounted(() => {
  if (projectId.value) {
    getTreeData()
  }
})

let ws: WebSocket | null = null
ws = new WebSocket(import.meta.env.VITE_APIWS_URL)
// ws = new WebSocket('ws://192.168.6.58:48080/apiTree')
if (ws) {
  //当WebSocket创建成功时，触发onopen事件
  ws.onopen = function () {
    //将消息发送到服务端
    ws?.send(JSON.stringify({ projectId: projectId.value }))
  }
}
/**
 * 获取树结构数据
 * */
const getTreeData = async () => {
  loading.value = true
  try {
    // let res = await DebugApi.getTree({ projectId: projectId.value })
    if (!ws) return
    if (ws.readyState === ws.OPEN) {
      // 已建立成功 直接发送消息
      ws.send(JSON.stringify({ projectId: projectId.value }))
    }
    ws.onmessage = function (e) {
      //当客户端收到服务端发来的消息时，触发onmessage事件，参数e.data包含server传递过来的数据
      let res = JSON.parse(e.data)
      // 场景引用接口或者用例时需要过滤
      if (props.isShowCheckBox) {
        treeData.value = filterTreeByTags(res ? [res] : [], props.filterList)
      } else {
        treeData.value = res ? [res] : []
      }
      debugStore.setTreeData(treeData.value)
    }
    ws.onerror = function (e) {
      //如果出现连接、处理、接收、发送数据失败的时候触发onerror事件
      console.log(e)
    }
  } catch (error) {
    console.log(error)
  } finally {
    loading.value = false
  }
}
/**
 * 节点点击
 * */
const handleNodeClick = async (data) => {
  if (!['接口', '用例'].includes(data.tag)) {
    defaultId.value = ''
    return
  }
  defaultId.value = data.id
  emit('node-click', treeData.value, data)
}

/** 更新节点后立即定位到某个节点 */
const handleFocusNode = async (info) => {
  debugger
  try {
    await getTreeData()
    const focusNode = findNodeByParams(treeData.value, info.name, info.tag, info.directId)
    focusNode && handleNodeClick(focusNode)
  } catch (error) {
    console.log(error)
  }
}

defineExpose({ handleNodeClick, getTreeData, handleFocusNode })
/**
 * 接口导入
 * */
const imported = () => {
  emit('import-interface')
}
/**
 * 新增目录
 * */
const append = (data) => {
  emit('add-directory', data)
}
/**
 * 重命名
 * */
const edit = (data) => {
  emit('edit-directory', data)
}
/**
 * 删除
 * */
const remove = async (data) => {
  try {
    // 删除的二次确认
    await message.delConfirm()
    loading.value = true
    // 发起删除
    if (data.tag == '接口') {
      await DebugApi.deleteApiInTree({ id: data.id, projectId: projectId.value })
    } else if (data.tag == '目录') {
      await DebugApi.deleteDirectInTree({ id: data.id, projectId: projectId.value })
    } else if (data.tag == '用例') {
      await DebugApi.deleteCaseInTree({ id: data.id, parentId: data.parentId, projectId: projectId.value })
    }
    message.success(t('common.delSuccess'))
    emit('delete-node', data)
    // 刷新列表
    await getTreeData()
  } catch (error) {
    console.log(error)
  } finally {
    loading.value = false
  }
}
/**
 * 新建接口
 * */
const createInterface = (data) => {
  emit('create-interface', data)
}
/**
 * 新增用例
 * */
const createCase = (data) => {
  emit('create-case', data)
}
/**
 * 避免popover闪现bug
 * */
const hidePopver = () => {
  popoverVisible.value = false
  setTimeout(() => {
    popoverVisible.value = true
  }, 10)
}
/** 触发选中 */
const changeCheckTreeNodes = () => {
  emit('selectNodes', treeRef.value.getCheckedNodes())
}
/** 拖动树后保存 */
const handleDrop = async (draggingNode: Node, dropNode: Node, position) => {
  loading.value = true
  const treeObj = treeData.value[0]
  try {
    let isModifySuccess = false
    if (position == 'inner') {
      isModifySuccess = findAndModifyNode(treeObj, draggingNode.data.id, dropNode.data.id)
    } else {
      isModifySuccess = findAndModifyNode(treeObj, draggingNode.data.id, dropNode.parent.data.id)
    }
    updateSort(treeData.value, draggingNode, dropNode)
    if (isModifySuccess) {
      await DebugApi.saveApiTree(treeObj)
      message.success(t('common.updateSuccess'))
    }
  } catch (error) {
    console.log(error)
  } finally {
    loading.value = false
  }
}
const updateSort = (nodes, draggingNode, dropNode) => {
  nodes.forEach((node, index) => {
    node.sort = index
    if (node.children && node.children.length > 0) {
      updateSort(node.children, draggingNode, dropNode)
    }
  })
}
/** 只有目录可以被拖入 */
const allowDrop = (draggingNode: Node, dropNode: Node, type: AllowDropType) => {
  const treeObj = treeData.value[0]
  if (dropNode.data.id == treeObj.id && type != 'inner') {
    message.warning('无法拖动至与根目录同级')
    return false
  }
  // 限制用例拖动
  const draggingParentId = draggingNode.parent ? draggingNode.parent.data.id : null;
  const dropParentId = dropNode.parent ? dropNode.parent.data.id : null;
  if (draggingParentId != dropParentId && draggingNode.data.tag == '用例') {
    return false
  }
  return !props.simple
}
/** 只有接口、目录可以被拖动 */
const allowDrag = (draggingNode: Node) => {
  // if (draggingNode.data.tag == '接口' || draggingNode.data.tag == '目录') {
  return !props.simple
  // }
  // return false
}
/** 新建markdown */
const createMarkdown = () => {
  emit('markdown')
}

</script>

<style lang="scss">
.tree-com {

  .current {
    display: block;
    background-color: var(--el-color-primary-light-9);
    width: 100%;
    position: relative;
    z-index: 1;

    small {
      color: #5e7ce0;
    }

    &::after {
      position: absolute;
      width: 350%;
      content: "";
      left: -150%;
      height: 100%;
      z-index: -1;
      background-color: var(--el-color-primary-light-9);
    }
  }

  .el-tree-node__content {
    .el-icon {
      z-index: 2;
    }
  }

  .el-tree-node__content:hover {
    background-color: var(--el-color-primary-light-9);
  }

  .el-tree-node:focus>.el-tree-node__content {
    background-color: var(--el-color-primary-light-9);
  }
}

.case-icon {
  margin: 0px 6px 0px 2px;
}

.tree-header {
  display: flex;
}

.method {
  font-style: normal;
  margin-right: 5px;
  font-size: 12px;
  font-weight: 900;
}

.operation-list {
  p {
    display: flex;
    align-items: center;
    border-bottom: 1px solid #ccc;
    padding-bottom: 10px;
    margin: 0 0 10px 0;
    cursor: pointer;

    .el-icon {
      margin-right: 10px;
    }

    &:last-child {
      border: none;
      margin-bottom: 0;
      padding: 0;
    }
  }
}
</style>
